﻿using System;
using System.Text;
using System.Security.Cryptography;

namespace Gigya.Socialize.SDK 
{
    /// <summary>
    /// This class is a utility class with static methods for calculating and validating cryptographic signatures.
    /// </summary>
    /// <remarks>author Raviv Pavel</remarks>
    public class SigUtils
    {
        /// <summary>
        /// Use this method to verify the authenticity of a 
        /// <a href="http://wiki.gigya.com/030_API_reference/020_REST_API/socialize.getUserInfo">socialize.getUserInfo</a> API method response,
        /// to make sure it is in fact originating from Gigya, and prevent fraud. 
        /// The "socialize.getUserInfo" API method response data include the following fields: 
        /// UID, signatureTimestamp (a timestamp) and UIDSignature (a cryptographic signature).
        /// Pass these fields as the corresponding parameters of this method, along with your partner's "Secret Key".
        /// Your secret key (provided in BASE64 encoding) is located at the bottom of the 
        /// <a href="http://www.gigya.com/site/partners/wfsocapi.aspx#&amp;&amp;userstate=SiteSetup">Site Setup</a> page on Gigya's website.
        /// The return value of the method indicates if the signature is valid (thus, originating from Gigya) or not.
        /// </summary>
        /// <param name="UID">pass the UID field returned by the "socialize.getUserInfo" API method response </param>
        /// <param name="timestamp">pass the signatureTimestamp field returned by the "socialize.getUserInfo" API method response </param>
        /// <param name="secret">your partner's "Secret Key", obtained from Gigya's website.</param>
        /// <param name="signature">pass the UIDSignature field returned by the "socialize.getUserInfo" API method response</param>
        /// <returns></returns>
        public static bool validateUserSignature(string UID, string timestamp, string secret, string signature)
        {
            string expectedSig = calcSignature(timestamp + "_" + UID, secret);
            return expectedSig.Equals(signature);
        }

        /// <summary>
        /// Use this method to verify the authenticity of a 
        /// <a href="http://wiki.gigya.com/030_API_reference/020_REST_API/socialize.getFriendsInfo">socialize.getFriendsInfo</a> API 
        /// method response, to make sure it is in fact originating from Gigya, and prevent fraud. 
        /// The "socialize.getFriendsInfo" API method response data include the following fields: 
        /// UID, signatureTimestamp (a timestamp) and friendshipSignature (a cryptographic signature).
        /// Pass these fields as the corresponding parameters of this method, along with your partner's "Secret Key". Your secret 
        /// key (provided in BASE64 encoding) is located at the bottom of the 
        /// <a href="http://www.gigya.com/site/partners/wfsocapi.aspx#&amp;&amp;userstate=SiteSetup">Site Setup</a> page on Gigya's website.
        /// The return value of the method indicates if the signature is valid (thus, originating from Gigya) or not.
        /// </summary>
        /// <param name="UID">pass the UID field returned by the "socialize.getFriendsInfo" API method response </param>
        /// <param name="timestamp">pass the signatureTimestamp field returned by the "socialize.getFriendsInfo" API method response </param>
        /// <param name="friendUID"></param>
        /// <param name="secret">your partner's "Secret Key", obtained from Gigya's website.</param>
        /// <param name="signature">pass the friendshipSignature field returned by the "socialize.getFriendsInfo" API method response</param>
        /// <returns></returns>
        public static bool validateFriendSignature(string UID, string timestamp, string friendUID, string secret, string signature)
        {
            string expectedSig = calcSignature(timestamp + "_" + friendUID + "_" + UID, secret);
            return expectedSig.Equals(signature);
        }

        /// <summary>
        /// This is a utility method for generating a cryptographic signature.
        /// </summary>
        /// <param name="text">the string for signing></param>
        /// <param name="key">the key for signing. Use your partner's "Secret Key", obtained from Gigya's website, as the signing key</param>
        /// <returns></returns>
        public static string calcSignature(string text, string key)
        {
            byte[] data = Encoding.UTF8.GetBytes(text);
            byte[] keyData = Convert.FromBase64String(key);

            // Compute signature for provided challenge and private key
            // Always use HMAC-SHA1 algorithm
            HMAC hmac = new HMACSHA1(keyData);

            byte[] macReciever = hmac.ComputeHash(data);
            return Convert.ToBase64String(macReciever);
        }

        /// <summary>
        /// This is a utility method for generating a base string for calculating the OAuth1 cryptographic signature.
        /// </summary>
        /// <param name="httpMethod">"POST" or "GET"</param>
        /// <param name="url">the full url without params</param>
        /// <param name="requestParams">list of params in the form of a GSDictionary</param>
        /// <returns>the base string to act on for calculating the OAuth1 cryptographic signature</returns>
        public static string calcOAuth1Basestring(string httpMethod, string url, GSDictionary requestParams)
        {
            // Normalize the URL per the OAuth requirements
            StringBuilder normalizedUrlSB = new StringBuilder();
            Uri u = new Uri(url);

            normalizedUrlSB.Append(u.Scheme.ToLowerInvariant());
            normalizedUrlSB.Append("://");
            normalizedUrlSB.Append(u.Host.ToLowerInvariant());
            if ((u.Scheme == "HTTP" && u.Port != 80) || (u.Scheme == "HTTPS" && u.Port != 443))
            {
                normalizedUrlSB.Append(':');
                normalizedUrlSB.Append(u.Port);
            }
            normalizedUrlSB.Append(u.LocalPath);


            // Create a sorted list of query parameters
            StringBuilder querystring = new StringBuilder();

            string currParam;
            foreach (string key in requestParams.getKeys())
            {
                if (requestParams.getString(key) != null)
                {
                    querystring.Append(key);
                    querystring.Append("%3D"); // URL encoded '='                    
                    currParam = GSRequest.UrlEncode(requestParams.getString(key));
                    if (!key.Equals("apiKey", StringComparison.InvariantCultureIgnoreCase) &&
                         !key.Equals("timestamp", StringComparison.InvariantCultureIgnoreCase) &&
                         !key.Equals("nonce", StringComparison.InvariantCultureIgnoreCase))
                    {
                        currParam = GSRequest.UrlEncode(currParam);
                    }
                    querystring.Append(currParam);
                    querystring.Append("%26"); // URL encoded '&'                   
                }
            }
            querystring.Remove(querystring.Length - 3, 3); 	// remove the last ecoded ampersand

            // Construct the base string from the HTTP method, the URL and the parameters 
            string basestring = httpMethod.ToUpperInvariant() + "&" + GSRequest.UrlEncode(normalizedUrlSB.ToString()) + "&" + querystring.ToString();
            return basestring;

        }

        internal static long CurrentTimeMillis()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
        }
    }
}
